package club.kingyin.easytoken.core.encryption;

import javax.crypto.Cipher;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

public class Rsa {

    private static final String KEY_RSA_TYPE = "RSA";
    private static final int KEY_SIZE = 1024;//JDK方式RSA加密最大只有1024位
    private static final int ENCODE_PART_SIZE = KEY_SIZE/8;
    public static final String PUBLIC_KEY_NAME = "public";
    public static final String PRIVATE_KEY_NAME = "private";

    /**
     * 创建公钥秘钥
     * @return
     */
    public static Map<String,String> createRSAKeys(){
        Map<String,String> keyPairMap = new HashMap<>();//里面存放公私秘钥的Base64位加密
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_RSA_TYPE);
            keyPairGenerator.initialize(KEY_SIZE,new SecureRandom());
            KeyPair keyPair = keyPairGenerator.generateKeyPair();

            //获取公钥秘钥
            String publicKeyValue = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
            String privateKeyValue = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());

            //存入公钥秘钥，以便以后获取
            keyPairMap.put(PUBLIC_KEY_NAME,publicKeyValue);
            keyPairMap.put(PRIVATE_KEY_NAME,privateKeyValue);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return keyPairMap;
    }

    public static String encode(String sourceStr, String publicKeyBase64Str) {
        return encode(sourceStr.getBytes(StandardCharsets.UTF_8), publicKeyBase64Str);
    }

    public static String encode(Object object, String publicKeyBase64Str) {
        return encode(toByteArray(object), publicKeyBase64Str);
    }

    /**
     * 公钥加密
     * 描述：
     *     1字节 = 8位；
     *     最大加密长度如 1024位私钥时，最大加密长度为 128-11 = 117字节，不管多长数据，加密出来都是 128 字节长度。
     * @param sourceBytes
     * @param publicKeyBase64Str
     * @return
     */
    public static String encode(byte[] sourceBytes,String publicKeyBase64Str){
        byte [] publicBytes = Base64.getDecoder().decode(publicKeyBase64Str);
        //公钥加密
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicBytes);
        List<byte[]> alreadyEncodeListData = new LinkedList<>();

        int maxEncodeSize = ENCODE_PART_SIZE - 11;
        String encodeBase64Result = null;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA_TYPE);
            PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(KEY_RSA_TYPE);
            cipher.init(Cipher.ENCRYPT_MODE,publicKey);
            int sourceLen = sourceBytes.length;
            for(int i=0;i<sourceLen;i+=maxEncodeSize){
                int curPosition = sourceLen - i;
                int tempLen = curPosition;
                if(curPosition > maxEncodeSize){
                    tempLen = maxEncodeSize;
                }
                byte[] tempBytes = new byte[tempLen];//待加密分段数据
                System.arraycopy(sourceBytes,i,tempBytes,0,tempLen);
                byte[] tempAlreadyEncodeData = cipher.doFinal(tempBytes);
                alreadyEncodeListData.add(tempAlreadyEncodeData);
            }
            int partLen = alreadyEncodeListData.size();//加密次数

            int allEncodeLen = partLen * ENCODE_PART_SIZE;
            byte[] encodeData = new byte[allEncodeLen];//存放所有RSA分段加密数据
            for (int i = 0; i < partLen; i++) {
                byte[] tempByteList = alreadyEncodeListData.get(i);
                System.arraycopy(tempByteList,0,encodeData,i*ENCODE_PART_SIZE,ENCODE_PART_SIZE);
            }
            encodeBase64Result = Base64.getEncoder().encodeToString(encodeData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encodeBase64Result;
    }

    /**
     * 私钥解密
     * @param sourceBase64RSA
     * @param privateKeyBase64Str
     */
    public static String decodeToString(String sourceBase64RSA,String privateKeyBase64Str){
        return new String(decode(sourceBase64RSA, privateKeyBase64Str), StandardCharsets.UTF_8);
    }

    // 解密
    public static byte[] decode(String sourceBase64RSA,String privateKeyBase64Str){
        byte[] privateBytes = Base64.getDecoder().decode(privateKeyBase64Str);
        byte[] encodeSource = Base64.getDecoder().decode(sourceBase64RSA);
        int encodePartLen = encodeSource.length/ENCODE_PART_SIZE;
        List<byte[]> decodeListData = new LinkedList<>();//所有解密数据
        byte [] decodeResultBytes = null;
        //私钥解密
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateBytes);
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA_TYPE);
            PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
            Cipher cipher = Cipher.getInstance(KEY_RSA_TYPE);
            cipher.init(Cipher.DECRYPT_MODE,privateKey);
            int allDecodeByteLen = 0;//初始化所有被解密数据长度
            for (int i = 0; i < encodePartLen; i++) {
                byte[] tempEncodedData = new byte[ENCODE_PART_SIZE];
                System.arraycopy(encodeSource,i*ENCODE_PART_SIZE,tempEncodedData,0,ENCODE_PART_SIZE);
                byte[] decodePartData = cipher.doFinal(tempEncodedData);
                decodeListData.add(decodePartData);
                allDecodeByteLen += decodePartData.length;
            }
            decodeResultBytes = new byte[allDecodeByteLen];
            for (int i = 0,curPosition = 0; i < encodePartLen; i++) {
                byte[] tempSorceBytes = decodeListData.get(i);
                int tempSourceBytesLen = tempSorceBytes.length;
                System.arraycopy(tempSorceBytes,0,decodeResultBytes,curPosition,tempSourceBytesLen);
                curPosition += tempSourceBytesLen;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return decodeResultBytes;
    }

    /**
     * 对象转数组
     * @param obj
     * @return
     */
    public static byte[] toByteArray (Object obj) {
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray ();
            oos.close();
            bos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }

    /**
     * 数组转对象
     * @param bytes
     * @return
     */
    public static Object toObject (byte[] bytes) {
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream (bytes);
            ObjectInputStream ois = new ObjectInputStream (bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (IOException | ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return obj;
    }

    public static void main(String[] args) {
        String p = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAK6axG/njuqqlg1tQQx7LG9ofGSztapHHGncdXYSz8cUqogPhOh2JayouVYQMOfHtZCWVML9BS3bfje2yf0PSC0ynzKJO7e3bXATJAQkckmiLK0sQ961RVCSTocQb1VQsdYXzW/CvocrXnqrfxtzVcT+Dvvaw9XARnmKs3uzbA6VAgMBAAECgYAuTzImk/3d05y0tvN3wfuJGOdVNizKms24bhZhWJ/vJ1YLfEI9/YgEgUOWxfiSoO9RB+43QTBsdzjEwiBUlAcKNNHS/FCC912Qewop5w+IFUdc1jNYaSa3Vy4TYPzPStPfHkHawqL6yBiSbOWNp6meeLAiqUEj+cs61I9liaNqiQJBAO8M251RvvHIep+bFIRurNCuJIhg7LGtIxixmgFF8/mfn6mWs55YSUuS9P9pL1EK7QBtyphdgm9dIzS0RBxNdN8CQQC6/B43l9wVfbnaeSnPxS3RLa0NzoYFnDw74d+rr8/sIS1MHoHfGAQJZWQwV6FMZTRjkS2WHatgAShjVkl0aBcLAkEA1vgSGnQjqRxnIVZ9aeZ5JCQmGDpJQDJ5iEBWROTaVNW15/Tsy+2K5BpglbrKYS/ZF3SkJsaYgHsaKNAfG/CPswJAA1l82RYZVTE5YRaGjzhDB9o/nV2zvOn9kj0LmrG9kcdOXmy1et+dQuXUE6GePBYcCCSTonrJlSCwLodfLXyn3QJBAMIgvQPNKjohjNOXsYxFsiVH4L1OOIg82vgLu6KU4Adfh+L0Emn6RcfsN5w3bUIDcleSTPMuadPSNhHH+ghC630=";
        String g = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCumsRv547qqpYNbUEMeyxvaHxks7WqRxxp3HV2Es/HFKqID4TodiWsqLlWEDDnx7WQllTC/QUt2343tsn9D0gtMp8yiTu3t21wEyQEJHJJoiytLEPetUVQkk6HEG9VULHWF81vwr6HK156q38bc1XE/g772sPVwEZ5irN7s2wOlQIDAQAB";
        String encode = encode("123", g);
        System.out.println(encode);
        String s = decodeToString("I4qzBX46bkY9ZMmZASNT0BJ8yIcCJf4OU/frOFMAO5pxPYmlcYDFm1qoVamUw2PdJtYxUkln+rJeqD91EaqWj2xaCo1oMNAqbzUTLN/dZ8YN/Ko5yvfRHtt9p2ES78CVKQ+Nl/WYE+QYGo0X7khw/n1C4R7O2dxS9+OsNFq3ZL8=", p);
        System.out.println(s);
    }
}
